﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Xml;
using System.Xml.XPath;

namespace Sherwood.Configuration
{
    public class XmlConfiguredPlugins : IPluginProvider
    {
        private readonly IConfigRepository _config;

        public XmlConfiguredPlugins(IConfigRepository config)
        {
            _config = config;
        }

        public IEnumerable<T> InstantiatePlugins<T>(string name, bool returnEmptyIfNotFound)
        {
            using (var configFile = _config.CreateXmlReaderSafe(name + ".xml"))
            {
                if (returnEmptyIfNotFound && configFile == null)
                    return new T[0];
                return GetConfiguredInstances<T>(configFile, "*/*", null);
            }
        }

        ///<summary>
        /// Will look through the given xml using the xpath selector and instantiate an instance of each configured type.
        /// If the instance implements <see cref="IXmlConfigurable"/> the <see cref="IXmlConfigurable.Configure"/> will be called
        /// on it with an <see cref="XmlReader"/> for the configuration node as paramter.
        ///</summary>
        ///<param name="xmlReader">The xml reader</param>
        ///<param name="elementXPath">The selector</param>
        ///<typeparam name="T">The type of instances to return. Instantiated objects are casted to this type before returned.</typeparam>
        ///<exception cref="ConfigurationErrorsException"></exception>
        /// <example>
        /// <para>Example XML:</para>
        /// <para>&lt;instances&gt;</para>
        /// <para>  &lt;add type="MyType, MyAssembly"&gt;</para>
        /// <para>    &lt;configuration /&gt;</para>
        /// <para>  &lt;/add&gt;</para>
        /// <para>&lt;/instances&gt;</para>
        /// </example>
        public static IEnumerable<T> GetConfiguredInstances<T>(XmlReader xmlReader, string elementXPath, IXmlNamespaceResolver namespaceResolver)
        {
            var reader = new XPathDocument(xmlReader);
            var instances = new List<T>();
            foreach (XPathNavigator node in reader.CreateNavigator().Select(elementXPath, namespaceResolver))
            {
                var type = Type.GetType(node.GetAttribute("type", String.Empty), true);
                var instance = DependencyInjection.Container.Resolve(type, null);
                if (instance is IXmlConfigurable)
                    ((IXmlConfigurable)instance).Configure(node.ReadSubtree());

                instances.Add((T)instance);
            }
            return instances;
        }
    }
}